package spbsu;


import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class ScheduleMaker {

    private static int n;
    private static Date from;
    private static Date to;
    private static Map<Integer, Subject> m;

    private static class informationVertex{
        int totalSubjColor;
        int usedSubjColor;
        boolean [] aSubjColor;
        boolean [] aTeachColor;
        ArrayList <Integer> aSubjColorList;
        ArrayList <Integer> aTeachColorList;

        informationVertex (ArrayList<Integer> aSubjColorList, ArrayList<Integer> aTeachColorList)
        {
            this.aSubjColor = new boolean[n + 1];
            this.aTeachColor = new boolean[n + 1];

            for (Integer i : aSubjColorList)
                this.aSubjColor[i] = true;

            for (Integer i : aTeachColorList)
                this.aTeachColor[i] = true;

            this.aSubjColorList = aSubjColorList;
            this.aTeachColorList = aTeachColorList;
            this.usedSubjColor = 0;
            this.totalSubjColor = aSubjColorList.size() - 1;
        }
    }

    private static class vertex{
        private vertex (int depth, informationVertex information, Group group)
        {
            this.depth = depth;
            this.information = information;
            this.subjColor = -1;
            this.teachColor = -1;
            this.to = new ArrayList<vertex>();
            this.group = group;
        }

        ArrayList<vertex> to;
        int subjColor;
        int teachColor;
        int depth;
        informationVertex information;
        Group group;

        void addEdge (vertex to)
        {
            this.to.add(to);
        }

        int getSubjColor ()
        {
            return this.subjColor;
        }

        int getTeachColor ()
        {
            return this.teachColor;
        }

        Group getGroup ()
        {
            return this.group;
        }

        void setColor (int subjColor, int teachColor)
        {
            if (this.subjColor > 0)
                this.information.usedSubjColor--;
            this.subjColor = subjColor;
            this.teachColor = teachColor;
            if (subjColor > 0)
                this.information.usedSubjColor++;
        }

        boolean checkColor (int subjColor, int teachColor)
        {
            if (!this.information.aSubjColor[subjColor] || !this.information.aTeachColor[teachColor])
                return false;

            if (subjColor == 0)
                return true;

            for (vertex to : this.to)
            {
                if (this.depth == to.depth)
                {
                    if (this.teachColor == to.teachColor)
                        return false;
                }
                else
                {
                    if (this.subjColor == to.subjColor)
                        return false;
                }
            }

            return false;
        }
    }

    static ArrayList <vertex> buildGraph (ArrayList <Group> groups)
    {
        ArrayList <vertex> everyGroupG [] = new ArrayList [groups.size()];
        ArrayList <Integer> everyGroupSubjColor [] = new ArrayList[groups.size()];
        ArrayList <Integer> everyGroupTeachColor [] = new ArrayList[groups.size()];

        for (int i = 0; i < groups.size(); i++)
        {
            everyGroupG[i] = new ArrayList<vertex>();
            everyGroupSubjColor[i] = new ArrayList<Integer>();
            everyGroupTeachColor[i] = new ArrayList<Integer>();
        }

        for (int i = 0; i < groups.size(); i++)
        {
            for (Subject s : groups.get(i).getSubjects())
            {
                everyGroupSubjColor[i].add(s.getId());
                everyGroupTeachColor[i].add(s.getTeacher().getId());
            }
            everyGroupSubjColor[i].add(0);
            everyGroupTeachColor[i].add(0);
        }



        for (int i = 0; i < groups.size(); i++)
        {
            for (int j = 0; j < ScheduleMaker.n; j++)
            {
                everyGroupG[i].add(new vertex(j, new informationVertex(everyGroupSubjColor[i], everyGroupTeachColor[i]), groups.get(i)));
            }
        }

        for (int i = 0; i < groups.size(); i++)
        {
            for (int j = 0; j < ScheduleMaker.n; j++)
                for (int k = j + 1; k < ScheduleMaker.n; k++)
                {
                    everyGroupG[i].get(j).to.add(everyGroupG[i].get(k));
                    everyGroupG[i].get(k).to.add(everyGroupG[i].get(j));
                }

            for (int j = i + 1; j < groups.size(); j++)
                for (int k = 0; k < ScheduleMaker.n; k++)
                {
                    everyGroupG[i].get(k).to.add(everyGroupG[j].get(k));
                    everyGroupG[j].get(k).to.add(everyGroupG[i].get(k));
                }
        }

        ArrayList <vertex> res = new ArrayList<vertex>();

        for (int i = 0; i < groups.size(); i++)
            for (int j = 0; j < ScheduleMaker.n; j++)
                res.add(everyGroupG[i].get(j));

        return res;
    }

    static Schedule buildSchedule (ArrayList <vertex> g)
    {
        ArrayList <Exam> exams = new ArrayList <Exam>();

        for (vertex v : g)
            if (v.subjColor > 0)
                exams.add(new Exam(m.get(v.subjColor), v.getGroup(), getDate(v.depth)));

        return new Schedule(exams);
    }

    static boolean findColoration (ArrayList <vertex> g, int n)
    {
        if (n == ScheduleMaker.n + 1)
        {
            for (vertex v : g)
            {
                if (v.information.usedSubjColor != v.information.totalSubjColor)
                    return false;
            }

            return true;
        }

        for (int i = 0; i < (g.get(n)).information.aSubjColorList.size(); i++)
        {
            if (g.get(n).checkColor(
                    (g.get(n)).information.aSubjColorList.get(i),
                    (g.get(n)).information.aTeachColorList.get(i)))
            {
                g.get(n).setColor((g.get(n)).information.aSubjColorList.get(i),
                                  (g.get(n)).information.aTeachColorList.get(i));
                if (findColoration(g, n + 1))
                    return true;
            }

        }

        return false;
    }

    static boolean findColoration (ArrayList <vertex> g)
    {
        return findColoration(g, 0);
    }

    static int getDayCount (Date from, Date to) //TODO implement
    {
        /*
        if (from.getYear() == to.getYear())
        {

        }
        else
        {

        }
        */

        return 4;
    }

    static Date getDate (int d) //TODO implement
    {
        return new Date ();
    }

    public static Schedule make(ArrayList <Group> groups, Date from, Date to)
    {
        ScheduleMaker.from = from;
        ScheduleMaker.to = to;
        ScheduleMaker.n = getDayCount(from, to);
        ScheduleMaker.m = new HashMap<Integer, Subject>();
        for (Group g : groups)
            for (Subject s : g.getSubjects())
            {
                if (!m.containsKey(s.getId())) //TODO check ???
                    m.put(s.getId(), s);
            }

        ArrayList <vertex> g = buildGraph(groups);

        if (findColoration(g))
        {
            return buildSchedule(g);
        }
        else
        {
            throw new IllegalArgumentException();
            //throw new Exception("Solution doesn't exist"); //TODO throw exception
        }
    }
}
